Skip to content

[CORE] : BUGFIX - prevent Stream closed on concurrent JAR access in TemplateManager#23596

Merged
wing328 merged 1 commit intoOpenAPITools:masterfrom
Picazsoo:bugfix/prevent-Stream-closed-on-concurrent-JAR-access-in-TemplateManager
Apr 21, 2026
Merged

[CORE] : BUGFIX - prevent Stream closed on concurrent JAR access in TemplateManager#23596
wing328 merged 1 commit intoOpenAPITools:masterfrom
Picazsoo:bugfix/prevent-Stream-closed-on-concurrent-JAR-access-in-TemplateManager

Conversation

@Picazsoo
Copy link
Copy Markdown
Contributor

@Picazsoo Picazsoo commented Apr 21, 2026

When two Gradle tasks run concurrently in isolated classloaders that both point to the same generator JAR, the JVM's JarFileFactory shares a single JarFile between both classloaders. If one classloader is closed/GC'd while the other is still copying a non-template resource (e.g. README.md), the shared JarFile is closed and the other worker's InflaterInputStream throws 'Stream closed'.

Two fixes in TemplateManager:

  1. getInputStream() now opens a fresh URLConnection with setUseCaches(false) for classpath resources, preventing the underlying JarFile from being shared across classloaders.

  2. write() now wraps the InputStream in try-with-resources, fixing the pre-existing resource leak where the stream was never closed after the binary file copy.

This bug was introduced by me as a part of the generator speedup in commit c07f3a0:
Before modernization: GenerateTask.generate() called DefaultGenerator(...).generate() directly on the Gradle worker thread — same classloader, no isolation, no concurrency issues.

After modernization: the task was refactored to use workerExecutor.classLoaderIsolation(). This runs each task in a separate isolated ClassLoader pointing to the same generator JAR. The JVM's JarURLConnection shares a single underlying JarFile per JAR URL across classloaders (via JarFileFactory). When two tasks run concurrently and one worker's classloader is torn down, it closes the shared JarFile, causing InflaterInputStream: Stream closed in the other worker.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request. - tagging @wing328

Summary by cubic

Prevents "Stream closed" errors when Gradle tasks access the same generator JAR concurrently by using non-cached URL connections for classpath resources and properly closing streams during file copy. Improves stability for isolated classloaders and stops flaky builds.

  • Bug Fixes
    • Open a fresh URLConnection with setUseCaches(false) for classpath resources to avoid shared JarFile across classloaders.
    • Wrap file copy streams in try-with-resources in write() to close them reliably and fix the leak.

Written for commit c30b9a3. Summary will update on new commits.

…Manager

When two Gradle tasks run concurrently in isolated classloaders that both
point to the same generator JAR, the JVM's JarFileFactory shares a single
JarFile between both classloaders. If one classloader is closed/GC'd while
the other is still copying a non-template resource (e.g. README.md), the
shared JarFile is closed and the other worker's InflaterInputStream throws
'Stream closed'.

Two fixes in TemplateManager:

1. getInputStream() now opens a fresh URLConnection with setUseCaches(false) for classpath resources, preventing the underlying JarFile from being shared across classloaders.

2. write() now wraps the InputStream in try-with-resources, fixing the pre-existing resource leak where the stream was never closed after the binary file copy.
@Picazsoo Picazsoo changed the title fix(core): prevent Stream closed on concurrent JAR access in TemplateManager [CORE] : BUGFIX - prevent Stream closed on concurrent JAR access in TemplateManager Apr 21, 2026
@Picazsoo Picazsoo marked this pull request as ready for review April 21, 2026 12:08
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file

@wing328
Copy link
Copy Markdown
Member

wing328 commented Apr 21, 2026

cc @OpenAPITools/generator-core-team

@wing328 wing328 merged commit 7c0d2d1 into OpenAPITools:master Apr 21, 2026
15 checks passed
@wing328 wing328 added this to the 7.22.0 milestone Apr 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants